package com.ws.client.feign;

import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;
import com.ws.client.controller.XmlUtils;
import com.ws.client.model.NameSpace;
import com.ws.client.utils.XMLUtil;
import feign.Response;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.jaxb.JAXBContextFactory;
import feign.soap.SOAPDecoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.context.annotation.Bean;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.soap.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.soap.SOAPFaultException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import static javax.xml.soap.SOAPConstants.DEFAULT_SOAP_PROTOCOL;

@Slf4j
public class WebXmlFeignClientConfiguration {

    private Charset charsetEncoding = StandardCharsets.UTF_8;
    private String soapProtocol = DEFAULT_SOAP_PROTOCOL;

    private String nameSpace = NameSpace.WEB_XML;

    private static final JAXBContextFactory jaxbContextFactory = new JAXBContextFactory.Builder()
            .withMarshallerJAXBEncoding("UTF-8")
            .build();

    /**
     * 新供指挥
     * <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wec="http://wechat.psucs.com">
     *     <soapenv:Header/>
     *     <soapenv:Body>
     *         <wec:$method>
     *             <!--Optional:-->
     *             <wec:msg>$param</wec:msg>
     *         </wec:$method>
     *     </soapenv:Body>
     * </soapenv:Envelope>
     * @return
     */
    @Bean
    public Encoder feignEncoder() {
        return (object, bodyType, template) -> {
            if (!(bodyType instanceof Class)) {
                throw new UnsupportedOperationException(
                        "SOAP only supports encoding raw types. Found " + bodyType);
            }
            try {
                Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
                Marshaller marshaller = jaxbContextFactory.createMarshaller((Class<?>) bodyType);
                // 格式化xml输出的格式
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                        Boolean.TRUE);
                marshaller.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
                    @Override
                    public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
                        if (namespaceUri.equals("http://WebXml.com.cn/")) return "web";
                        return suggestion;
                    }
                });
                marshaller.marshal(object, document);
                Element rootElement = document.getDocumentElement();
                //创建soap工厂
                SOAPFactory soapFactory = SOAPFactory.newInstance();

                SOAPMessage soapMessage = MessageFactory.newInstance(soapProtocol).createMessage();
                SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
                //自定义前缀
                soapEnvelope.setPrefix("soap");
                soapEnvelope.removeNamespaceDeclaration("SOAP-ENV");
                soapMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, charsetEncoding.displayName());

                //添加消息体以及json字符串
                SOAPHeader header = soapMessage.getSOAPHeader();
                header.setPrefix("soap");
                //添加消息体以及json字符串
                SOAPBody body = soapMessage.getSOAPBody();
                body.setPrefix("soap");
                body.addTextNode(XMLUtil.toXML(object));

                soapMessage.saveChanges();
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                soapMessage.writeTo(bos);
                String outParam = new String(bos.toByteArray(),StandardCharsets.UTF_8.toString());
                System.out.println(StringEscapeUtils.unescapeHtml(outParam));
                template.body(StringEscapeUtils.unescapeHtml(outParam));
            } catch (SOAPException | JAXBException | ParserConfigurationException | IOException | TransformerFactoryConfigurationError e) {
                e.printStackTrace();
                throw new EncodeException(e.toString(), e);
            }
        };
    }

    @Bean
    public Decoder feignDecoder() {
        return (Response response, Type type)->{
            if (response.status() == 404)
                return Util.emptyValueOf(type);
            if (response.body() == null)
                return null;
            while (type instanceof ParameterizedType) {
                ParameterizedType ptype = (ParameterizedType) type;
                type = ptype.getRawType();
            }
            if (!(type instanceof Class)) {
                throw new UnsupportedOperationException(
                        "SOAP only supports decoding raw types. Found " + type);
            }

            try {
                SOAPMessage message = MessageFactory.newInstance(soapProtocol).createMessage(null,
                                response.body().asInputStream());
                if (message.getSOAPBody() != null) {
                    if (message.getSOAPBody().hasFault()) {
                        throw new SOAPFaultException(message.getSOAPBody().getFault());
                    }

                    //XML转字符串
                    TransformerFactory tf   =   TransformerFactory.newInstance();
                    Transformer t = tf.newTransformer();
                    t.setOutputProperty("encoding","GB23121");//解决中文问题，试过用GBK不行
                    ByteArrayOutputStream   bos =new ByteArrayOutputStream();
                    t.transform(new DOMSource(message.getSOAPBody().extractContentAsDocument()),
                            new StreamResult(bos));
                    String xmlStr = bos.toString();
                    //创建一个StringReader将xml报文转成流-忽略命名空间，否则会解析出来的值为空
                    StringReader sr = new StringReader(xmlStr);
                    SAXParserFactory sax = SAXParserFactory.newInstance();
                    sax.setNamespaceAware(false);
                    XMLReader xmlReader = sax.newSAXParser().getXMLReader();
                    Source source = new SAXSource(xmlReader, new InputSource(sr));
                    return jaxbContextFactory.createUnmarshaller((Class<?>) type)
                            .unmarshal(source);
                }
            } catch (SOAPException | JAXBException  |SAXException|TransformerConfigurationException e) {
                throw new DecodeException(response.status(), e.toString(), e);
            }catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (TransformerException e) {
                e.printStackTrace();
            } finally {
                if (response.body() != null) {
                    response.body().close();
                }
            }
            return Util.emptyValueOf(type);
        };
    }

    public static  String  judgeNull(String str){
        return (str==null)?"":str;
    }
}


